use std::collections::HashMap;

use crate::gen_title;

pub fn ch4_3_1() {
    gen_title("ch4_3_1", 18);

    let arr = [1, 2, 3];

    for v in arr {
        println!("{}", v);
    }
}

pub fn ch4_3_2() {
    gen_title("ch4_3_2", 18);

    let v1 = vec![1, 2, 3];
    let v1_iter = v1.iter();

    for val in v1_iter {
        println!("{}", val);
    }
}

pub fn ch4_3_3() {
    gen_title("ch4_3_3", 18);

    let arr = [1, 2, 3];
    let mut arr_iter = arr.into_iter();

    assert_eq!(arr_iter.next(), Some(1));
    assert_eq!(arr_iter.next(), Some(2));
    assert_eq!(arr_iter.next(), Some(3));
    assert_eq!(arr_iter.next(), None);

    println!("arr_iter: {:?}", arr_iter);
}

pub fn ch4_3_4() {
    gen_title("ch4_3_4", 18);

    let values = vec![1, 2, 3];
    {
        let result = match IntoIterator::into_iter(values) {
            mut iter => loop {
                match iter.next() {
                    Some(x) => {
                        println!("{}", x);
                    }
                    None => break,
                }
            },
        };
        result
    }
}

pub fn ch4_3_5() {
    gen_title("ch4_3_5", 18);

    let values = vec![1, 2, 3];

    for v in values.into_iter() {
        println!("{}", v);
    }

    let values = vec![1, 2, 3];
    let _values_iter = values.iter();

    println!("values: {:?}", values);

    let mut values = vec![1, 2, 3];
    let mut values_iter_mut = values.iter_mut();

    if let Some(v) = values_iter_mut.next() {
        *v = 0;
    }

    println!("values: {:?}", values);
}

pub fn ch4_3_6() {
    gen_title("ch4_3_6", 18);

    let v1 = vec![1, 2, 3];
    let v1_iter = v1.iter();
    let total: i32 = v1.iter().sum();

    assert_eq!(total, 6);
    println!("total: {}", total);

    println!("{:?}", v1);
}

pub fn ch4_3_7() {
    gen_title("ch4_3_7", 18);

    let names = ["sunface", "sunfei"];
    let ages = [18, 18];

    let folks: HashMap<_, _> = names.into_iter().zip(ages.into_iter()).collect();

    println!("{:?}", folks);
}

pub fn ch4_3_8() {
    gen_title("ch4_3_8", 18);

    #[derive(Debug)]
    struct Shoe {
        size: u32,
        style: String,
    }

    fn shoes_in_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
        shoes.into_iter().filter(|s| s.size == shoe_size).collect()
    }

    let shoes = vec![
        Shoe {
            size: 10,
            style: String::from("sneaker"),
        },
        Shoe {
            size: 13,
            style: String::from("sandal"),
        },
        Shoe {
            size: 10,
            style: String::from("boot"),
        },
    ];

    let in_my_size = shoes_in_size(shoes, 10);

    println!("{:?}", in_my_size);
}

pub fn ch4_3_9() {
    gen_title("ch4_3_9", 18);

    #[derive(Debug)]
    struct Counter {
        count: u32,
    }

    impl Counter {
        fn new() -> Counter {
            Counter { count: 0 }
        }
    }

    impl Iterator for Counter {
        type Item = u32;

        fn next(&mut self) -> Option<Self::Item> {
            if self.count < 5 {
                self.count += 1;
                Some(self.count)
            } else {
                None
            }
        }
    }

    let mut counter = Counter::new();
    assert_eq!(counter.next(), Some(1));
    println!("{:?}", counter);
    assert_eq!(counter.next(), Some(2));
    println!("{:?}", counter);
    assert_eq!(counter.next(), Some(3));
    println!("{:?}", counter);
    assert_eq!(counter.next(), Some(4));
    println!("{:?}", counter);
    assert_eq!(counter.next(), Some(5));
    println!("{:?}", counter);
    assert_eq!(counter.next(), None);
    println!("{:?}", counter);

    let sum: u32 = Counter::new()
        .zip(Counter::new().skip(1))
        .map(|(a, b)| a * b)
        .filter(|x| x % 3 == 0)
        .sum();

    println!("{}", sum);

    for i in Counter::new() {
        println!("{}", i);
    }
}

pub fn ch4_3_10() {
    gen_title("ch4_3_10", 18);

    let v = vec![1, 2, 3, 4, 5, 6];

    let val = v
        .iter()
        .enumerate()
        .filter(|&(idx, _)| idx % 2 == 0)
        .map(|(_, val)| val)
        .fold(0, |sum, acm| sum + acm);

    println!("{}", val);
}
